
import java.util.*;

import jade.core.*;

public class ScenarioGenerator
{
	private String traderBaseName;
	private int traderCount;
	
	private AID[] traders;
	
	private int initialTraderBudget;
	private int stepTraderBudget;
	
	private int minAgentAbilities;
	private int maxAgentAbilities;
	
	private int minBuyingHabits;
	private int maxBuyingHabits;
	
	private int productTypeCount;
	
	private int minProductQuality;
	private int maxProductQuality;
	private DistributionType qualityDistribution;
	
	private int minProductPrice;
	private int maxProductPrice;
	private DistributionType priceDistribution;
	
	private HashMap<AID,AgentDetail> agentDetails;
	
	ScenarioGeneratorGUI scenarioGeneratorGUI;
	
	private boolean detailsDone;
	private Object lock;
	
	public ScenarioGenerator(String traderBaseName, int traderCount, Object lock)
	{
		this.setTraderBaseName(traderBaseName);
		this.setTraderCount(traderCount);
		this.detailsDone = false;
		this.lock = lock;
		
		agentDetails = new HashMap<AID, AgentDetail>();
		
		generateTraderAIDs();
		
		scenarioGeneratorGUI = new ScenarioGeneratorGUI(this, lock);
	}
	
	public AID[] getTradersAID()
	{
		return traders;
	}
	
	private void generateAgentDetails()
	{
		Random typeGenerator = new Random(System.currentTimeMillis() + (long)(Math.random()*10000));
		Random unfaithfullGenerator = new Random(System.currentTimeMillis() + (long)(Math.random()*10000));
		Random productionProbabilityGenerator = new Random(System.currentTimeMillis() + (long)(Math.random()*10000)); 
		RandomGenerator priceGenerator = new RandomGenerator(minProductPrice, maxProductPrice);
		RandomGenerator qualityGenerator = new RandomGenerator(minProductQuality, maxProductQuality);
		
		for(AID trader : traders)
		{
			double unfaithfullSeller = Math.abs(unfaithfullGenerator.nextGaussian()*0.2);
			double unfaithfullBuyer = Math.abs(unfaithfullGenerator.nextGaussian()*0.2);
			AgentDetail currentAgentDetail = new AgentDetail(
					trader,
					unfaithfullSeller,
					unfaithfullBuyer);
			
			// generate agent production abilities
			int generatedTypes[] = new int[productTypeCount];
			Random generator = new Random(System.currentTimeMillis() + (long)(Math.random()*10000));
			int productionAbilitiesCount = generator.nextInt(maxAgentAbilities-minAgentAbilities+1) + minAgentAbilities;
			for(int i = 0; i < productionAbilitiesCount; i++)
			{
				int typeId = typeGenerator.nextInt(productTypeCount);
				while(generatedTypes[typeId] == 1)
					typeId = typeGenerator.nextInt(productTypeCount);
				generatedTypes[typeId] = 1;
				
				String type = "type"+typeId;
				double productionProbability = productionProbabilityGenerator.nextDouble();
				int productQuality = qualityGenerator.nextInt(qualityDistribution);
				int productPrice = priceGenerator.nextInt(priceDistribution);
				
				currentAgentDetail.addProductionAbility(new ProductionAbility(type, productionProbability, productQuality, productPrice));
			}
			
			
			// generate buying habits
			int buyingHabitsCount = generator.nextInt(maxBuyingHabits-minBuyingHabits+1) + minBuyingHabits;
			if(buyingHabitsCount > productTypeCount - productionAbilitiesCount)
				buyingHabitsCount = productTypeCount - productionAbilitiesCount;
			for(int i = 0; i < buyingHabitsCount; i++)
			{
				int typeId = typeGenerator.nextInt(productTypeCount);
				while(generatedTypes[typeId] == 1)
					typeId = typeGenerator.nextInt(productTypeCount);
				generatedTypes[typeId] = 1;
				
				String type = "type"+typeId;
				int minQuality = qualityGenerator.nextInt(qualityDistribution);
				int maxPrice = priceGenerator.nextInt(priceDistribution);
				
				currentAgentDetail.addPurchaseHabit(new PurchaseHabit(type, minQuality, maxPrice));
			}
			
			agentDetails.put(trader, currentAgentDetail);
		}
	}
	
	public String getInitMessageContent(AID trader)
	{
		String content = "";
		
		if(!detailsDone)
		{
			generateAgentDetails();
			detailsDone = true;
		}
		
		if(!isValidAID(trader))
			return content;
		
		content = initialTraderBudget + " " + stepTraderBudget + " ";
		
		AgentDetail agentDetail = agentDetails.get(trader);
		content += agentDetail.unfaithfullSeller + " " + agentDetail.unfaithfullBuyer+" ";
		content += agentDetail.productionAbilities.size() + " ";
		for(ProductionAbility ability : agentDetail.productionAbilities)
			content += ability.productType + " " + ability.productionProbability + " " + ability.productionQuality + " " + ability.productionPrice + " ";
		for(PurchaseHabit habit : agentDetail.purchaseHabits)
			content += habit.productType + " " + habit.minQuality + " " + habit.maxPrice + " ";
		
		return content;
	}
	
	private void generateTraderAIDs()
	{
		traders = new AID[traderCount];
		traders[0] = new AID(traderBaseName,AID.ISLOCALNAME);
		for (int i = 1; i < traderCount; i++)
			traders[i] = new AID(traderBaseName+"_"+i,AID.ISLOCALNAME);
	}
	
	private boolean isValidAID(AID testedAID)
	{
		for (int i = 0; i < traders.length; i++) {
			if(traders[i].getLocalName().equals(testedAID.getLocalName()))
				return true;
		}
		return false;
	}
	
	public void setTraderCount(int traderCount) {
		this.traderCount = traderCount;
	}

	public int getTraderCount() {
		return traderCount;
	}

	public void setTraderBaseName(String traderBaseName) {
		this.traderBaseName = traderBaseName;
	}

	public String getTraderBaseName() {
		return traderBaseName;
	}
	
	public void setInitialTraderBudget(int initialTraderBudget) {
		this.initialTraderBudget = initialTraderBudget;
	}

	public void setStepTraderBudget(int stepTraderBudget) {
		this.stepTraderBudget = stepTraderBudget;
	}

	public void setProductTypeCount(int productTypeCount) {
		this.productTypeCount = productTypeCount;
	}

	public void setMinAgentAbilities(int minAgentAbilities) {
		this.minAgentAbilities = minAgentAbilities;
	}

	public void setMaxAgentAbilities(int maxAgentAbilities) {
		this.maxAgentAbilities = maxAgentAbilities;
	}

	public void setMinProductQuality(int minProductQuality) {
		this.minProductQuality = minProductQuality;
	}

	public void setMaxProductQuality(int maxProductQuality) {
		this.maxProductQuality = maxProductQuality;
	}

	public void setMinProductPrice(int minProductPrice) {
		this.minProductPrice = minProductPrice;
	}

	public void setMaxProductPrice(int maxProductPrice) {
		this.maxProductPrice = maxProductPrice;
	}

	public void setQualityDistribution(DistributionType qualityDistribution) {
		this.qualityDistribution = qualityDistribution;
	}

	public void setPriceDistribution(DistributionType priceDistribution) {
		this.priceDistribution = priceDistribution;
	}

	public int getMinBuyingHabits() {
		return minBuyingHabits;
	}

	public int getMaxBuyingHabits() {
		return maxBuyingHabits;
	}

	public void setMinBuyingHabits(int minBuyingHabits) {
		this.minBuyingHabits = minBuyingHabits;
	}

	public void setMaxBuyingHabits(int maxBuyingHabits) {
		this.maxBuyingHabits = maxBuyingHabits;
	}

	private class RandomGenerator
	{
		private double minValue;
		private double maxValue;
		
		private double mean;
		private double deviation;
		
		Random random;
		
		public RandomGenerator(double minValue, double maxValue)
		{
			this.minValue = minValue;
			this.maxValue = maxValue;
			
			if(minValue > maxValue)
			{
				double xchg = maxValue;
				maxValue = minValue;
				minValue = xchg;
			}
			
			mean = (maxValue - minValue)/2.0 + minValue;
			deviation = (maxValue - minValue)/4.0;
			
			random = new Random(System.currentTimeMillis() + (long)(Math.random()*10000));
		}
		
		public RandomGenerator(double minValue, double maxValue, double mean, double deviation)
		{
			this.minValue = minValue;
			this.maxValue = maxValue;
			this.mean = mean;
			this.deviation = deviation;
			
			if(minValue > maxValue)
			{
				double xchg = maxValue;
				maxValue = minValue;
				minValue = xchg;
			}
		}
		
		
		@SuppressWarnings("unused")
		public double nextDouble(DistributionType type)
		{
			switch(type)
			{
			case GAUSSIAN:
				return random.nextGaussian()*deviation + mean;
			case UNIFORM:
				return random.nextDouble();
			default:
				return 0;
			}
		}
		
		public int nextInt(DistributionType type)
		{
			switch(type)
			{
			case GAUSSIAN:
				return (int)(random.nextGaussian()*deviation + mean);
			case UNIFORM:
				return random.nextInt((int)(maxValue-minValue+1.0)) + (int)minValue;
			default:
				return 0;
			}
		}
	}
	
	private class ProductionAbility
	{
		String productType;
		double productionProbability;
		int productionQuality;
		int productionPrice;
		
		public ProductionAbility(String type, double probability, int quality, int price)
		{
			productType = type;
			productionProbability = probability;
			productionQuality = quality;
			productionPrice = price;
		}
	}
	
	private class PurchaseHabit
	{
		String productType;
		int minQuality;
		int maxPrice;
		
		public PurchaseHabit(String type, int minQ, int maxP)
		{
			productType = type;
			minQuality = minQ;
			maxPrice = maxP;
		}
	}
	
	public class AgentDetail
	{
		private AID agentAID;
		
		private Vector<ProductionAbility> productionAbilities;
		private Vector<PurchaseHabit> purchaseHabits;
		
		private double unfaithfullSeller;
		private double unfaithfullBuyer;
		
		public AgentDetail(AID agentAID, double unfaithfullSeller, double unfaithfullBuyer)
		{
			this.agentAID = agentAID;
			productionAbilities = new Vector<ProductionAbility>();
			purchaseHabits = new Vector<PurchaseHabit>();
			
			this.unfaithfullBuyer = unfaithfullBuyer;
			this.unfaithfullSeller = unfaithfullSeller;
		}
		
		public void addProductionAbility(ProductionAbility ability) {
			productionAbilities.add(ability);
		}
		
		public void addPurchaseHabit(PurchaseHabit habit) {
			purchaseHabits.add(habit);
		}

		public AID getAgentAID() {
			return agentAID;
		}

		public Vector<ProductionAbility> getProductionAbilities() {
			return productionAbilities;
		}

		public Vector<PurchaseHabit> getPurchaseHabits() {
			return purchaseHabits;
		}

		public double getUnfaithfullSeller() {
			return unfaithfullSeller;
		}

		public double getUnfaithfullBuyer() {
			return unfaithfullBuyer;
		}
	}
	
	enum DistributionType
	{
		GAUSSIAN,
		UNIFORM
	}
}
